
package com.weiqiao.api.until;

import cn.hutool.extra.qrcode.BufferedImageLuminanceSource;
import com.google.zxing.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeWriter;
import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.net.ssl.HttpsURLConnection;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Iterator;

/**
 * 图片处理工具类
 *
 * @author ChenYF
 * @Description
 */
public class ImageUtil {

    /**
     * 指定大小 压缩图片
     * 默认输出50%质量图片
     *
     * @param in      输出流
     * @param width   图片长度
     * @param height  图片宽度
     * @param quality 图片压缩质量 范围：0.0~1.0，1为最高质量
     * @param scale   按照比例进行缩放 0.0~1.0
     * @return
     * @throws IOException
     */
    public static InputStream compress(InputStream in, Integer width, Integer height, Float quality, Double scale) throws IOException {
        if (in == null) {
            return null;
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Thumbnails.Builder builder = Thumbnails.of(in);
        if (width != null && height != null) {
            builder.size(width.intValue(), height.intValue());
        }

        if (scale != null) {
            builder.scale(scale.doubleValue());
        }
        if (quality != null) {
            builder.outputQuality(quality.floatValue());
        }
        builder.toOutputStream(os);
        builder.keepAspectRatio(false);//严格按宽高输出　
        InputStream is = new ByteArrayInputStream(os.toByteArray());
        in.close();
        os.close();
        return is;
    }

    /**
     * 保存文件到服务器临时路径(用于文件上传)
     *
     * @param fileName
     * @param is
     * @return 文件全路径
     */
    public static String writeFile(String fileName, InputStream is) {
        if (fileName == null || fileName.trim().length() == 0) {
            return null;
        }
        try {
            /** 首先保存到临时文件 */
            FileOutputStream fos = new FileOutputStream(fileName);
            byte[] readBytes = new byte[512];// 缓冲大小
            int readed = 0;
            while ((readed = is.read(readBytes)) > 0) {
                fos.write(readBytes, 0, readed);
            }
            fos.close();
            is.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileName;
    }

    /**
     * 合成图片并返回图片
     *
     * @param pressimag  插图
     * @param targetimag 底图
     * @param x          X轴偏移位置
     * @param y          Y轴偏移位置
     */
    public final static BufferedImage pictureSynthesis(Image pressimag, Image targetimag, int x, int y) {
        try {
            //目标文件
            int width = targetimag.getWidth(null);
            int height = targetimag.getHeight(null);
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(targetimag, 0, 0, width, height, null);

            g = image.createGraphics();

            //水印文件
            int wideth_biao = pressimag.getWidth(null);
            int height_biao = pressimag.getHeight(null);
            g.drawImage(pressimag, x, y, wideth_biao, height_biao, null);
            return image;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 图片添加文字
     *
     * @param text            --文字
     * @param backgroundImage -- 目标图片
     * @param fontName        -- 字体名
     * @param fontStyle       -- 字体样式 0:正常  1.粗体
     * @param color           -- 字体颜色
     * @param fontSize        -- 字体大小
     * @param x               -- X轴偏移位置
     * @param y               -- Y轴偏移位置
     */
    public static BufferedImage pictureAddText(String text, Image backgroundImage, String fontName, int fontStyle, Color color, int fontSize, int x, int y) {
        try {
            int width = backgroundImage.getWidth(null);
            int height = backgroundImage.getHeight(null);
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
            Graphics2D g = image.createGraphics();
            g.drawImage(backgroundImage, 0, 0, width, height, null);

            //消除锯齿
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));

            g.drawString(text, x, y);
            g.dispose();

            return image;
        } catch (Exception e) {
            System.out.println(e);
            return null;
        }
    }

    /**
     * 修改图片的宽高
     *
     * @param itemp  需要处理的图片流
     * @param height 目标高度像素
     * @param width  目标宽度像素
     * @return
     */
    public static BufferedImage upImgWidthAndHeight(Image itemp, int height, int width) {
        double ratio = 0.0; // 缩放比例
        itemp = itemp.getScaledInstance(width, height, itemp.SCALE_SMOOTH);//itemp.SCALE_SMOOTH  选择图像平滑度比缩放速度具有更高优先级的图像缩放算法。
        BufferedImage bi = toBufferedImage(itemp);
        // 计算比例
        if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
            double ratioHeight = (new Integer(height)).doubleValue() / bi.getHeight();
            double ratioWhidth = (new Integer(width)).doubleValue() / bi.getWidth();
            if (ratioHeight > ratioWhidth) {
                ratio = ratioHeight;
            } else {
                ratio = ratioWhidth;
            }
            AffineTransformOp op = new AffineTransformOp(AffineTransform//仿射转换
                    .getScaleInstance(ratio, ratio), null);//返回表示剪切变换的变换
            itemp = op.filter(bi, null);//转换源 BufferedImage 并将结果存储在目标 BufferedImage 中。
        }
        return toBufferedImage(itemp);
    }


    /**
     * 设置图片为圆角
     *
     * @param imag         图片
     * @param targetSize   边长
     * @param cornerRadius 圆角半径
     * @return
     */
    public static BufferedImage roundImage(Image imag, int targetSize, int cornerRadius) {
        BufferedImage outputImage = new BufferedImage(targetSize, targetSize, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = outputImage.createGraphics();
        g2.setComposite(AlphaComposite.Src);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.WHITE);
        g2.fill(new RoundRectangle2D.Float(0, 0, targetSize, targetSize, cornerRadius, cornerRadius));
        g2.setComposite(AlphaComposite.SrcAtop);
        g2.drawImage(imag, 0, 0, null);
        g2.dispose();
        return outputImage;
    }


    /**
     * 按比例对图片进行缩放.
     *
     * @param imag  原图
     * @param scale 缩放比例
     * @return
     * @throws IOException
     */
    public static BufferedImage zoomByScale(Image imag, double scale) throws IOException {
        int _width = (int) (scale * imag.getWidth(null));
        int _height = (int) (scale * imag.getHeight(null));
        //获取缩放后的Image对象
        Image _img = imag.getScaledInstance(_width, _height, Image.SCALE_DEFAULT);
        //新建一个和Image对象相同大小的画布
        BufferedImage image = new BufferedImage(_width, _height, BufferedImage.TYPE_INT_ARGB);
        //获取画笔
        Graphics2D graphics = image.createGraphics();
        //将Image对象画在画布上,最后一个参数,ImageObserver:接收有关 Image 信息通知的异步更新接口,没用到直接传空
        graphics.drawImage(_img, 0, 0, null);
        //释放资源
        graphics.dispose();

        return image;
    }


    /**
     * 将文件流保存到本地位置
     *
     * @param inputStream 文件流
     * @param path        保存地址
     * @param fileName    文件名
     */
    public static String savePic(InputStream inputStream, String path, String fileName) {

        OutputStream os = null;
        try {
            // 2、保存到临时文件
            // 1K的数据缓冲
            byte[] bs = new byte[1024];
            // 读取到的数据长度
            int len;
            // 输出的文件流保存到本地文件
            File tempFile = new File(path);
            if (!tempFile.exists()) {
                tempFile.mkdirs();
            }
            os = new FileOutputStream(tempFile.getPath() + File.separator + fileName);
            // 开始读取
            while ((len = inputStream.read(bs)) != -1) {
                os.write(bs, 0, len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 完毕，关闭所有链接
            try {
                os.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return path + File.separator + fileName;
    }

    /**
     * 将网络地址转为io流
     *
     * @param fileUrl 网络文件地址
     * @return
     * @throws IOException
     */
    public static InputStream urlToIs(String fileUrl) throws IOException {
        URL url = new URL(fileUrl);
        HttpURLConnection uc = (HttpURLConnection) url.openConnection();
        uc.setDoInput(true);//设置是否要从 URL 连接读取数据,默认为true
        uc.connect();
        return uc.getInputStream();
    }


    /**
     * Image -> BufferedImage
     *
     * @param image
     * @return
     */
    public static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }
        image = new ImageIcon(image).getImage();
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(
                    image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
        }

        if (bimage == null) {
            int type = BufferedImage.TYPE_INT_RGB;
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }
        Graphics g = bimage.createGraphics();

        g.drawImage(image, 0, 0, null);
        g.dispose();

        return bimage;
    }

    /**
     * 讀取二維碼中的攜帶內容
     *
     * @param ins 文件流
     * @return
     */
    public static String readQRCode(InputStream ins) {
        String identifyContent = ""; // 二维码识别内容

        MultiFormatReader multiFormatReader = new MultiFormatReader();
        HashMap hints = new HashMap();
        hints.put(EncodeHintType.CHARACTER_SET, "GBK");
        try {
            /*
            TODO： JPEGImageDecoder 和 JPEGCodec 为 Apache 私有jar包 无法进行部署
            JPEGImageDecoder decoderFile = JPEGCodec.createJPEGDecoder(ins);
            BufferedImage source = decoderFile.decodeAsBufferedImage();
            */
            BufferedImage source =  ImageIO.read(ins);
            BinaryBitmap binaryImg = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(source)));
            Result result = multiFormatReader.decode(binaryImg, hints);
            identifyContent = result.getText();
            System.out.println("識別內容：" + identifyContent);
        } catch (Exception e) {
            System.out.println("識別報錯！");
            return identifyContent;
        } finally {
            return identifyContent;
        }
    }

    /**
     * 生成二维码信息
     * @param text 二维码内容
     * @param width 二维码宽度
     * @param height 二维码高度
     * @param filePath 保存地址
     * @throws WriterException
     * @throws IOException
     */
    public static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height);
        Path path = FileSystems.getDefault().getPath(filePath);
        FileUtil.checkPath(filePath);
        MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path);
    }



    /**
     * 生成源图片的处理图
     *
     * @param srcFile 源图片路径
     * @param dstFile 目标图片路径
     */
    private static void replace(String srcFile, String dstFile) {
        try {
            URL http;
            if (srcFile.trim().startsWith("https")) {
                http = new URL(srcFile);
                HttpsURLConnection conn = (HttpsURLConnection) http.openConnection();
                conn.setRequestMethod("GET");
            } else if (srcFile.trim().startsWith("http")) {
                http = new URL(srcFile);
                HttpURLConnection conn = (HttpURLConnection) http.openConnection();
                conn.setRequestMethod("GET");
            } else {
                http = new File(srcFile).toURI().toURL();
            }
            BufferedImage bi = ImageIO.read(http.openStream());
            if (bi != null) {
                removeWatermark(bi);
                exportImage(bi, srcFile, dstFile);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 由ImageIO流生成源图片的处理图
     *
     * @param bi       ImageIO
     * @param fileName 源图片带后缀的文件名
     * @param dstFile  目标图片路径
     */
    private static void exportImage(BufferedImage bi, String fileName, String dstFile) {
        try {
            String type = fileName.substring(fileName.lastIndexOf(".") + 1);
            Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName(type);
            ImageWriter writer = it.next();
            File f = new File(dstFile);
            ImageOutputStream ios = ImageIO.createImageOutputStream(f);
            writer.setOutput(ios);
            writer.write(bi);
            bi.flush();
            ios.flush();
            ios.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 去除水印
     */
    private static void removeWatermark(BufferedImage bi) {
        Color wColor = new Color(254, 254, 254);
        Color hColor = new Color(197, 196, 191);
        //白底水印
        for (int i = 0; i < bi.getWidth(); i++) {
            for (int j = 0; j < bi.getHeight(); j++) {
                int color = bi.getRGB(i, j);
                Color oriColor = new Color(color);
                int red = oriColor.getRed();
                int greed = oriColor.getGreen();
                int blue = oriColor.getBlue();
                if (red == 254 && greed == 254 && blue == 254) {
                    continue;
                }
                if (red > 220 && greed > 180 && blue > 80) {
                    bi.setRGB(i, j, wColor.getRGB());
                }
                if (red <= 240 && greed >= 200 && blue >= 150) {
                    bi.setRGB(i, j, wColor.getRGB());
                }
            }
        }
    }

    public static void main(String[] args) {
        replace("D:/443/a1.jpg","D:/443/b1.png");
    }
}
